/** * Copyright 2015-2016 Red Hat, Inc, and individual contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.wildfly.swarm.cdi.jaxrsapi.runtime; import java.util.List; import javax.enterprise.context.ApplicationScoped; import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.ClassInfo; import org.jboss.jandex.DotName; import org.jboss.jandex.Index; import org.jboss.jandex.MethodInfo; import org.jboss.shrinkwrap.api.Archive; import org.jboss.shrinkwrap.api.asset.ByteArrayAsset; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.wildfly.swarm.client.jaxrs.ServiceClient; import org.wildfly.swarm.jaxrs.JAXRSArchive; import org.wildfly.swarm.spi.api.ArchiveMetadataProcessor; /** * @author Ken Finnigan */ @ApplicationScoped public class ServiceClientProcessor implements ArchiveMetadataProcessor { @Override public void processArchive(Archive<?> archive, Index index) { List<ClassInfo> serviceClients = index.getKnownDirectImplementors((DotName.createSimple(ServiceClient.class.getName()))); serviceClients.forEach(info -> { String name = info.name().toString() + "_generated"; String path = "WEB-INF/classes/" + name.replace('.', '/') + ".class"; archive.as(JAXRSArchive.class).add(new ByteArrayAsset(ClientServiceFactory.createImpl(name, info)), path); }); } private static class ClientServiceFactory implements Opcodes { static byte[] createImpl(String implName, ClassInfo classInfo) { ClassWriter cw = new ClassWriter(0); MethodVisitor mv; AnnotationVisitor av0; cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, implName.replace('.', '/'), null, "java/lang/Object", new String[]{classInfo.name().toString().replace('.', '/')} ); int lastDot = implName.lastIndexOf('.'); String simpleName = implName.substring(lastDot + 1); cw.visitSource(simpleName + ".java", null); { av0 = cw.visitAnnotation("Ljavax/enterprise/context/ApplicationScoped;", true); av0.visitEnd(); } cw.visitInnerClass("javax/ws/rs/client/Invocation$Builder", "javax/ws/rs/client/Invocation", "Builder", ACC_PUBLIC + ACC_STATIC + ACC_ABSTRACT + ACC_INTERFACE); { mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); mv.visitCode(); Label l0 = new Label(); mv.visitLabel(l0); mv.visitLineNumber(14, l0); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false); Label l1 = new Label(); mv.visitLabel(l1); mv.visitLineNumber(15, l1); mv.visitInsn(RETURN); Label l2 = new Label(); mv.visitLabel(l2); mv.visitLocalVariable("this", buildTypeDef(implName), null, l0, l2, 0); mv.visitMaxs(1, 1); mv.visitEnd(); } List<AnnotationInstance> annotations = classInfo.annotations().get(DotName.createSimple("org.wildfly.swarm.client.jaxrs.Service")); String baseUrl = (String) annotations.get(0).value("baseUrl").value(); int lineNum = 18; classInfo.asClass().methods() .stream() .forEachOrdered(method -> { createMethod(cw, implName, classInfo.name().toString(), method, lineNum, baseUrl); }); cw.visitEnd(); return cw.toByteArray(); } static void createMethod(ClassWriter cw, String implName, String clientInterfaceName, MethodInfo method, int lineNum, String baseUrl) { MethodVisitor mv; { mv = cw.visitMethod(ACC_PUBLIC, method.name(), buildMethodDef(method), null, null); mv.visitCode(); Label l0 = new Label(); mv.visitLabel(l0); mv.visitLineNumber(lineNum++, l0); mv.visitLdcInsn(Type.getType(buildTypeDef(clientInterfaceName))); mv.visitTypeInsn(NEW, "org/jboss/resteasy/client/jaxrs/ResteasyClientBuilder"); mv.visitInsn(DUP); mv.visitMethodInsn(INVOKESPECIAL, "org/jboss/resteasy/client/jaxrs/ResteasyClientBuilder", "<init>", "()V", false); mv.visitMethodInsn(INVOKEVIRTUAL, "org/jboss/resteasy/client/jaxrs/ResteasyClientBuilder", "build", "()Lorg/jboss/resteasy/client/jaxrs/ResteasyClient;", false); mv.visitLdcInsn(baseUrl); Label l1 = new Label(); mv.visitLabel(l1); mv.visitLineNumber(lineNum++, l1); mv.visitMethodInsn(INVOKEVIRTUAL, "org/jboss/resteasy/client/jaxrs/ResteasyClient", "target", "(Ljava/lang/String;)Lorg/jboss/resteasy/client/jaxrs/ResteasyWebTarget;", false); Label l2 = new Label(); mv.visitLabel(l2); mv.visitLineNumber(lineNum - 2, l2); mv.visitMethodInsn(INVOKESTATIC, "org/wildfly/swarm/cdi/jaxrsapi/deployment/ProxyBuilder", "builder", "(Ljava/lang/Class;Ljavax/ws/rs/client/WebTarget;)Lorg/wildfly/swarm/cdi/jaxrsapi/deployment/ProxyBuilder;", false); Label l3 = new Label(); mv.visitLabel(l3); mv.visitLineNumber(lineNum++, l3); mv.visitMethodInsn(INVOKEVIRTUAL, "org/wildfly/swarm/cdi/jaxrsapi/deployment/ProxyBuilder", "build", "()Ljava/lang/Object;", false); mv.visitTypeInsn(CHECKCAST, clientInterfaceName.replace('.', '/')); for (int i = 1; i <= method.parameters().size(); i++) { mv.visitVarInsn(ALOAD, i); } Label l4 = new Label(); mv.visitLabel(l4); mv.visitLineNumber(lineNum++, l4); mv.visitMethodInsn(INVOKEINTERFACE, clientInterfaceName.replace('.', '/'), method.name(), buildMethodDef(method), true); Label l5 = new Label(); mv.visitLabel(l5); if (method.returnType().kind().equals(org.jboss.jandex.Type.Kind.VOID)) { mv.visitLineNumber(lineNum++, l5); mv.visitInsn(RETURN); } else { mv.visitLineNumber(lineNum - 4, l5); mv.visitInsn(ARETURN); } Label l6 = new Label(); mv.visitLabel(l6); int methodParams = 0; mv.visitLocalVariable("this", buildTypeDef(implName), null, l0, l6, methodParams++); for (AnnotationInstance anno : method.annotations()) { if (anno.name().toString().contains("QueryParam") || anno.name().toString().contains("PathParam")) { short position = anno.target().asMethodParameter().position(); org.jboss.jandex.Type parameterType = anno.target().asMethodParameter().method().parameters().get(position); mv.visitLocalVariable(String.valueOf(anno.value().value()), buildTypeDef(parameterType.name().toString()), null, l0, l6, methodParams++); } } mv.visitMaxs(3, methodParams); lineNum += 4; mv.visitEnd(); } } static String buildTypeDef(String name) { return "L" + name.replace('.', '/') + ";"; } static String buildMethodDef(MethodInfo method) { StringBuilder builder = new StringBuilder(); // Method Parameters builder.append("("); for (org.jboss.jandex.Type type : method.parameters()) { builder.append(buildTypeDef(type.name().toString())); } builder.append(")"); // Method Return Type if (method.returnType().kind().equals(org.jboss.jandex.Type.Kind.VOID)) { builder.append("V"); } else { builder.append(buildTypeDef(method.returnType().name().toString())); } return builder.toString(); } } }